package com.an.blog.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.an.blog.admin.domain.BlogArticle;
import com.an.blog.admin.domain.BlogComment;
import com.an.blog.admin.domain.BlogTalk;
import com.an.blog.admin.domain.bo.BlogCommentBo;
import com.an.blog.admin.domain.vo.BlogCommentVo;
import com.an.blog.mapper.BlogArticleMapper;
import com.an.blog.mapper.BlogCommentMapper;
import com.an.blog.mapper.BlogTalkMapper;
import com.an.blog.service.IBlogCommentService;
import com.an.blog.web.domain.bo.BlogCommentQueryWebBo;
import com.an.blog.web.domain.vo.BlogCommentWebVo;
import com.an.common.constant.BlogConstants;
import com.an.common.core.domain.PageQuery;
import com.an.common.core.domain.entity.SysUser;
import com.an.common.core.page.TableDataInfo;
import com.an.common.exception.blog.BlogException;
import com.an.common.helper.LoginHelper;
import com.an.common.utils.ServletUtils;
import com.an.common.utils.StreamUtils;
import com.an.common.utils.StringUtils;
import com.an.common.utils.ip.AddressUtils;
import com.an.system.mapper.SysUserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 博客评论Service业务层处理
 *
 * @author Ann
 * @date 2023-05-08
 */
@RequiredArgsConstructor
@Service
public class BlogCommentServiceImpl implements IBlogCommentService {

    public static final int CONTENT_MAX_SIZE = 30;
    private final BlogCommentMapper baseMapper;

    private final SysUserMapper userMapper;

    private final BlogArticleMapper articleMapper;

    private final BlogTalkMapper talkMapper;

    /**
     * 查询博客评论列表
     *
     * @param bo        业务对象
     * @param pageQuery 页面查询
     * @return {@link TableDataInfo }<{@link BlogCommentVo }>
     * @author: Ann
     * @createDate: 2023/06/25 19:49:25
     * @version: 1.0.0
     */
    @Override
    public TableDataInfo<BlogCommentVo> queryPageList(BlogCommentBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<BlogComment> lqw = buildQueryWrapper(bo);
        Page<BlogComment> result = baseMapper.selectPage(pageQuery.build(), lqw);
        long total = result.getTotal();
        //列表信息
        List<BlogComment> records = result.getRecords();
        //评论人id
        List<Long> userIds = StreamUtils.toList(records, BlogComment::getUserId);
        List<SysUser> users = getUserInfoByUserIds(userIds);
        //分类用户昵称，用户头像
        Map<Long, String> nickNameMap = StreamUtils.groupByKey2StringValue(users, SysUser::getUserId, SysUser::getNickName);
        Map<Long, String> avatarMap = StreamUtils.groupByKey2StringValue(users, SysUser::getUserId, SysUser::getAvatar);

        //评论主题为文章的map
        Map<Long, String> articleMap = getTopicMap(records, BlogConstants.COMMENT_ARTICLE_TOPIC);
        //为说说的map
        Map<Long, String> talkMap = getTopicMap(records, BlogConstants.COMMENT_TALK_TOPIC);

        List<BlogCommentVo> collect = records.stream().map(comment -> {
            BlogCommentVo blogCommentVo = new BlogCommentVo();
            blogCommentVo.setCommentId(comment.getCommentId());
            blogCommentVo.setCommentContent(comment.getCommentContent());
            blogCommentVo.setStatus(comment.getStatus());
            blogCommentVo.setTopic(comment.getTopic());
            blogCommentVo.setIpSource(comment.getIpSource());
            blogCommentVo.setLikedCount(comment.getLikedCount());
            blogCommentVo.setCreateTime(comment.getCreateTime());
            blogCommentVo.setAvatar(avatarMap.get(comment.getUserId()));
            blogCommentVo.setNickName(nickNameMap.get(comment.getUserId()));
            blogCommentVo.setAnswerNickName(nickNameMap.get(comment.getAnswerId()));
            if (blogCommentVo.getTopic().equals(BlogConstants.COMMENT_ARTICLE_TOPIC)) {
                blogCommentVo.setTopicContent(articleMap.get(comment.getTopicId()));
            }
            if (blogCommentVo.getTopic().equals(BlogConstants.COMMENT_TALK_TOPIC)) {
                blogCommentVo.setTopicContent(talkMap.get(comment.getTopicId()));
            }
            return blogCommentVo;
        }).collect(Collectors.toList());

        return TableDataInfo.build(collect,total);
    }


    /**
     * 得到主题Map
     *
     * @param records   记录
     * @param topicName 主题名称
     * @return {@link List }<{@link Long }>
     * @author: Ann
     * @createDate: 2023/06/16 09:50:49
     * @version: 1.0.0
     */
    private Map<Long, String> getTopicMap(List<BlogComment> records, String topicName) {
        Map<String, List<BlogComment>> topicCategories = records.stream()
            .collect(Collectors.groupingBy(BlogComment::getTopic));
        List<Long> topicIds = topicCategories.getOrDefault(topicName, Collections.emptyList())
            .stream().map(BlogComment::getTopicId).collect(Collectors.toList());
        if (BlogConstants.COMMENT_ARTICLE_TOPIC.equals(topicName) && CollUtil.isNotEmpty(topicIds)) {
            List<BlogArticle> blogArticles = articleMapper.selectList(new LambdaQueryWrapper<BlogArticle>().select(BlogArticle::getArticleId,
                BlogArticle::getArticleTitle).in(BlogArticle::getArticleId, topicIds));
            blogArticles = blogArticles.stream().peek(article -> {
                if (article.getArticleTitle().length() > CONTENT_MAX_SIZE) {
                    article.setArticleTitle(article.getArticleTitle().substring(0, 30) + "...");
                }
            }).collect(Collectors.toList());
            return StreamUtils.groupByKey2StringValue(blogArticles, BlogArticle::getArticleId, BlogArticle::getArticleTitle);
        }
        if (BlogConstants.COMMENT_TALK_TOPIC.equals(topicName) && CollUtil.isNotEmpty(topicIds)) {
            List<BlogTalk> blogTalks = talkMapper.selectList(new LambdaQueryWrapper<BlogTalk>()
                .select(BlogTalk::getTalkId, BlogTalk::getTalkContent)
                .in(BlogTalk::getTalkId, topicIds));
            blogTalks = blogTalks.stream().peek(talk -> {
                if (talk.getTalkContent().length() > CONTENT_MAX_SIZE) {
                    talk.setTalkContent(talk.getTalkContent().substring(0, 30) + "...");
                }
            }).collect(Collectors.toList());
            return StreamUtils.groupByKey2StringValue(blogTalks, BlogTalk::getTalkId, BlogTalk::getTalkContent);
        }
        //扩展
        return Collections.emptyMap();
    }

    /**
     * 查询博客评论列表
     */
    @Override
    public List<BlogCommentVo> queryList(BlogCommentBo bo) {
        LambdaQueryWrapper<BlogComment> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<BlogComment> buildQueryWrapper(BlogCommentBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<BlogComment> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, BlogComment::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getTopic()), BlogComment::getTopic, bo.getTopic());
        lqw.eq(bo.getTopicId() != null, BlogComment::getTopicId, bo.getTopicId());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), BlogComment::getStatus, bo.getStatus());
        lqw.eq(bo.getLikedCount() != null, BlogComment::getLikedCount, bo.getLikedCount());
        lqw.orderByAsc(BlogComment::getTopicId);
        lqw.orderByAsc(BlogComment::getCreateTime);
        return lqw;
    }

    /**
     * 批量删除博客评论
     *
     * @param ids     id
     * @param isValid 是有效
     * @return {@link Boolean }
     * @author: Ann
     * @createDate: 2023/06/16 10:45:20
     * @version: 1.0.0
     */
    @Override
    public Boolean deleteWithValidByIds(List<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        //检查是否有子评论，一并删除
        List<BlogComment> blogComments = baseMapper.selectList(new LambdaQueryWrapper<BlogComment>().select(BlogComment::getCommentId)
            .in(BlogComment::getParentId, ids));
        List<Long> commentIds = StreamUtils.toList(blogComments, BlogComment::getCommentId);
        commentIds.addAll(ids);
        return baseMapper.deleteBatchIds(commentIds) > 0;
    }

    /**
     * 评论
     *
     * @param blogComment 博客评论
     * @return {@link Boolean }
     * @author: Ann
     * @createDate: 2023/06/08 15:53:50
     * @version: 1.0.0
     */
    @Override
    public Boolean comment(BlogComment blogComment) {
        Long userId = LoginHelper.getUserId();
        blogComment.setUserId(userId);
        String clientIp = ServletUtils.getClientIP();
        blogComment.setIpAddress(clientIp);

        blogComment.setIpSource(AddressUtils.getRealProvinceByIp(clientIp));
        if (ObjectUtil.isNull(blogComment.getAnswerId())) {
            blogComment.setParentId(BlogConstants.COMMENT_TOP_LEVEL);
        }
        return baseMapper.insert(blogComment) > 0;
    }

    /**
     * 查询数通过类型和主题id
     *
     * @param commentArticleType 评论文章类型
     * @param topicId            主题id
     * @return {@link Long }
     * @author: Ann
     * @createDate: 2023/06/08 16:28:49
     * @version: 1.0.0
     */
    @Override
    public Long queryCountByTypeAndTopicId(String commentArticleType, Long topicId) {
        return baseMapper.selectCount(new LambdaQueryWrapper<BlogComment>().eq(BlogComment::getTopic, commentArticleType)
            .eq(BlogComment::getTopicId, topicId));
    }

    /**
     * 查询网站列表
     *
     * @param commentQueryWebBo 评论查询网络业务对象
     * @return {@link List }<{@link BlogCommentWebVo }>
     * @author: Ann
     * @createDate: 2023/06/09 08:05:47
     * @version: 1.0.0
     */
    @Override
    public List<BlogCommentWebVo> queryWebList(BlogCommentQueryWebBo commentQueryWebBo) {
        List<BlogComment> blogComments = baseMapper.selectList(new LambdaQueryWrapper<BlogComment>()
            .eq(BlogComment::getTopicId, commentQueryWebBo.getTopicId())
            .eq(BlogComment::getTopic, commentQueryWebBo.getTopic()));
        //1.获取用户昵称，头像信息
        List<Long> userIds = StreamUtils.toList(blogComments, BlogComment::getUserId);
        //用户数据
        List<SysUser> users = getUserInfoByUserIds(userIds);
        Map<Long, String> nickNameMap = StreamUtils.groupByKey2StringValue(users, SysUser::getUserId, SysUser::getNickName);
        Map<Long, String> avatarMap = StreamUtils.groupByKey2StringValue(users, SysUser::getUserId, SysUser::getAvatar);
        //转换前端对象
        List<BlogCommentWebVo> blogCommentWebVos = BeanUtil.copyToList(blogComments, BlogCommentWebVo.class);
        //构建层级，一共只构建两级
        return blogCommentWebVos.stream()
            .filter(blogCommentWebVo -> blogCommentWebVo.getParentId().equals(BlogConstants.COMMENT_TOP_LEVEL))
            .peek(blogCommentWebVo -> {
                blogCommentWebVo.setAvatar(avatarMap.get(blogCommentWebVo.getUserId()));
                blogCommentWebVo.setNickName(nickNameMap.get(blogCommentWebVo.getUserId()));
                blogCommentWebVo.setChildren(buildWebChildren(blogCommentWebVo.getCommentId(),
                    blogCommentWebVos, avatarMap, nickNameMap));
            }).collect(Collectors.toList());
    }

    /**
     * 删除评论网站
     *
     * @param commentId 评论id
     * @return {@link Boolean }
     * @author: Ann
     * @createDate: 2023/06/10 17:01:51
     * @version: 1.0.0
     */
    @Override
    public Boolean deleteCommentOnWeb(Long commentId) {

        //门户删除评论，判断是否删除的是自己的评论
        validBeforeDelete(commentId);
        //检查是否有子评论，一并删除
        List<BlogComment> blogComments = baseMapper.selectList(new LambdaQueryWrapper<BlogComment>().select(BlogComment::getCommentId)
            .eq(BlogComment::getParentId, commentId));
        List<Long> commentIds = StreamUtils.toList(blogComments, BlogComment::getCommentId);
        commentIds.add(commentId);
        return baseMapper.deleteBatchIds(commentIds) > 0;
    }

    /**
     * 删除之前有效
     *
     * @param commentId 评论id
     * @author: Ann
     * @createDate: 2023/06/10 17:11:44
     * @version: 1.0.0
     */
    private void validBeforeDelete(Long commentId) {
        //获取用户id
        Long userId = LoginHelper.getUserId();
        BlogComment blogComment = baseMapper.selectById(commentId);
        //不是自己的评论
        assert userId != null;
        if (!userId.equals(blogComment.getUserId())) {
            throw new BlogException("blog.web.comment.delete.not.current.user");
        }
    }

    /**
     * 构建web孩子
     *
     * @param commentId         评论id
     * @param blogCommentWebVos 博客评论web vos
     * @param avatarMap         《阿凡达》地图
     * @param nickNameMap       尼克名字映射
     * @return {@link List }<{@link BlogCommentWebVo }>
     * @author: Ann
     * @createDate: 2023/06/09 09:06:25
     * @version: 1.0.0
     */
    private List<BlogCommentWebVo> buildWebChildren(Long commentId, List<BlogCommentWebVo> blogCommentWebVos,
                                                    Map<Long, String> avatarMap, Map<Long, String> nickNameMap) {
        return blogCommentWebVos.stream().filter(blogCommentWebVo -> commentId.equals(blogCommentWebVo.getParentId()))
            .peek(blogCommentWebVo -> {
                blogCommentWebVo.setAvatar(avatarMap.get(blogCommentWebVo.getUserId()));
                blogCommentWebVo.setNickName(nickNameMap.get(blogCommentWebVo.getUserId()));
                blogCommentWebVo.setAnswerAvatar(avatarMap.get(blogCommentWebVo.getAnswerId()));
                blogCommentWebVo.setAnswerNickName(nickNameMap.get(blogCommentWebVo.getAnswerId()));
            })
            .collect(Collectors.toList());
    }

    /**
     * 获取用户信息
     *
     * @param userIds 用户id
     * @return {@link List }<{@link SysUser }>
     * @author: Ann
     * @createDate: 2023/06/09 08:16:22
     * @version: 1.0.0
     */
    private List<SysUser> getUserInfoByUserIds(List<Long> userIds) {
        return userMapper.selectList(new LambdaQueryWrapper<SysUser>().select(SysUser::getUserId,
                SysUser::getNickName, SysUser::getAvatar)
            .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds));
    }

}
